/*
 * Calculator.cpp
 *
 * Performs all calculations within the system
 *
 * Author: Zachary Masiello
 * Author: Peter-John Rowe
 */

#include "Calculator.h"

#include <unistd.h>

// Constructor
Calculator::Calculator(SensorInterrupt *newSensorInterrupt, LEDLight *newLEDLight)
{
	sensorInterrupt = newSensorInterrupt;
	ledLight = newLEDLight;
	calcMode = 'm';
	calcOn = false;
	sensorInterrupt->calcDistance(false);
	units = NULL;
	circumference = 210;
	numCalculations = 0;
	summationOfSpeeds = 0;
	isMoving = true;
	currentSpeed = 0;
	averageSpeed = 0;
	distance = 0;
	previousDistance = 0;
	ledLight->autoLightStatus('n');
	allCount = 0;
	startTime = time(0);
	numTimesNoUpdate = 0;
	ledLight->setUnitLightOnOff(false);
}

// Destructor
Calculator::~Calculator()
{
	// TODO Auto-generated destructor stub
}

// For thread to control calculations
void* Calculator::run(Calculator * calculator)
{
	while(true)
	{
		if(calculator->calcMode == 'm')
		{
			if(calculator->calcOn == true)
			{
				calculator->ledLight->autoLightStatus('b');
				calculator->distance = calculator->calculateDistance();
				if(calculator->distance > calculator->previousDistance)
				{
					calculator->numTimesNoUpdate = 0;
					calculator->ledLight->setWheelLightBlink(true);
					calculator->isMoving = true;
					calculator->previousDistance = calculator->distance;
					calculator->currentSpeed = calculator->calculateCurrentSpeed(true);
					calculator->averageSpeed = calculator->calculateAverageSpeed();
				}
				else if(calculator->numTimesNoUpdate >= 2)
				{
					calculator->ledLight->setWheelLightBlink(false);
					calculator->isMoving = false;
					calculator->currentSpeed = 0;
					calculator->numCalculations++;
					calculator->averageSpeed = calculator->calculateAverageSpeed();
				}
				else
				{
					calculator->numTimesNoUpdate++;
					calculator->previousDistance = calculator->distance;
					calculator->currentSpeed = calculator->calculateCurrentSpeed(true);
					calculator->averageSpeed = calculator->calculateAverageSpeed();
				}
			}
			else//(calcOn == false)
			{
				calculator->ledLight->autoLightStatus('n');
				if(calculator->sensorInterrupt->getAllCount() > calculator->allCount)
				{
					calculator->numTimesNoUpdate = 0;
					calculator->ledLight->setWheelLightBlink(true);
					calculator->isMoving = true;
					calculator->currentSpeed = calculator->calculateCurrentSpeed(false);
				}
				else if(calculator->numTimesNoUpdate >= 2)
				{
					calculator->currentSpeed = 0;
					calculator->ledLight->setWheelLightBlink(false);
					calculator->isMoving = false;
				}
				else
				{
					calculator->numTimesNoUpdate++;
					calculator->currentSpeed = calculator->calculateCurrentSpeed(false);
				}
			}
		}
		else//(calcMode == 'a')
		{
			if(calculator->isMoving)
			{
				calculator->ledLight->autoLightStatus('b');
				calculator->ledLight->setWheelLightBlink(true);
				calculator->distance = calculator->calculateDistance();
				if(calculator->distance > calculator->previousDistance)
				{
					calculator->numTimesNoUpdate = 0;
					calculator->previousDistance = calculator->distance;
					calculator->currentSpeed = calculator->calculateCurrentSpeed(true);
					calculator->averageSpeed = calculator->calculateAverageSpeed();
				}
				else if(calculator->numTimesNoUpdate >= 2)
				{
					calculator->elapsedTime += difftime(time(0), calculator->startTime);
					calculator->isMoving = false;
					calculator->currentSpeed = 0;
				}
				else
				{
					calculator->numTimesNoUpdate++;
					calculator->previousDistance = calculator->distance;
					calculator->currentSpeed = calculator->calculateCurrentSpeed(true);
					calculator->averageSpeed = calculator->calculateAverageSpeed();
				}
			}
			else//(!calculator->isMoving)
			{
				calculator->ledLight->autoLightStatus('o');
				calculator->ledLight->setWheelLightBlink(false);
				calculator->distance = calculator->calculateDistance();
				if(calculator->distance > calculator->previousDistance)
				{
					calculator->startTime = time(0);
					calculator->previousDistance = calculator->distance;
					calculator->isMoving = true;
					calculator->currentSpeed = calculator->calculateCurrentSpeed(true);
					calculator->averageSpeed = calculator->calculateAverageSpeed();
				}
			}
		}
		calculator->allCount = calculator->sensorInterrupt->getAllCount();
		usleep(500000);//sleep .5 seconds
	}
}

// Calculate Distance
double Calculator::calculateDistance()
{
	//(circum(cm) * wheel turns) * (1km/100000cm)
	double tmpDistance = (circumference * sensorInterrupt->getCount());
	tmpDistance = tmpDistance / 100000;
	if(units == KILOMETERS)
	{
		return tmpDistance;
	}
	else if(units == MILES)
	{
		//1 kilometer = 0.621371 miles
		return tmpDistance * 0.621371;
	}
}

// Calculate current speed
double Calculator::calculateCurrentSpeed(bool addToAvg)
{
	//formula from given excel sheet, broken up because qnx isn't following Order of Operations!!!!
	double time = sensorInterrupt->getTime();
	if(time == 0)//Nothing has happened yet(no sensor triggers)
	{
		return 0;
	}
	double tmp1 = (double)circumference / (double)100000;
	double tmp2 = (double)3600 / sensorInterrupt->getTime();
	double tmp3 = tmp1 * tmp2;
	double curSpeed = tmp3;
	if(units == KILOMETERS)
	{
		//curSpeed = curSpeed;
	}
	else if(units == MILES)
	{
		curSpeed = curSpeed * 0.621371;
	}
	if(addToAvg == true)
	{
		summationOfSpeeds = summationOfSpeeds + curSpeed;
		numCalculations++;
	}
	return curSpeed;
}

// Calculate average speed
double Calculator::calculateAverageSpeed()
{
	if(numCalculations == 0)//Nothing has happened yet(no sensor triggers)
	{
		return 0;
	}
	double avgSpeed = summationOfSpeeds / (double)numCalculations;

	return avgSpeed;
}

// Get current speed
double Calculator::getCurrentSpeed()
{
	return currentSpeed;
}

// Get Average Speed
double Calculator::getAverageSpeed()
{
	return averageSpeed;
}

// Get the distance
double Calculator::getDistance()
{
	return distance;
}

// Gets the circumference
int Calculator::getCircumference()
{
	tmpCircumference = circumference;
	return tmpCircumference;
}

// Increment the tire cirumference
void Calculator::incrementTmpTireCircum()
{
	tmpCircumference++;
	if(tmpCircumference > 220)
	{
		tmpCircumference = 190;
	}
}

// Sets the temporary wheel circumference value
void Calculator::setTmpAsPerCircum()
{
	circumference = tmpCircumference;
}

// Reset user data
void Calculator::dataReset()
{
	numCalculations = 0;
	summationOfSpeeds = 0;
	currentSpeed = 0;
	averageSpeed = 0;
	distance = 0;
	previousDistance = 0;
	sensorInterrupt->resetDistance();
	elapsedTime = 0;
	startTime = time(0);
}

// Reset ALL data
void Calculator::fullReset()
{
	dataReset();
	calcMode = 'm';
	calcOn = false;
	sensorInterrupt->calcDistance(false);
	units = NULL;
	circumference = 210;
}

void Calculator::setCircumference(int newCircumference)
{
	if(newCircumference <= 220 && newCircumference >= 190)
	{
		circumference = newCircumference;
	}
	else
	{
		return;
	}
}

// Change calculation mode
void Calculator::changeCalcMode()
{
	if(calcMode == 'm')
	{
		calcMode = 'a';
		sensorInterrupt->calcDistance(true);
		if(calcOn)
		{
			elapsedTime += difftime(time(0), startTime);
			startTime = time(0);
		}
	}
	else
	{
		calcMode = 'm';
		calcOn = false;
	}
}

// Gets the calc mode
char  Calculator::getCalcMode()
{
	return calcMode;
}

// Gets the elapsed time
int Calculator::getElapsedTime()
{
	if(isMoving)
	{
		return elapsedTime + (int)difftime(time(0), startTime);
	}
	else
	{
		return elapsedTime;
	}
}

// Start/stop calculations
void Calculator::changeCalcOnOff()
{
	if(calcOn == true)
	{
		elapsedTime += (int)difftime(time(0), startTime);
		sensorInterrupt->calcDistance(false);
		calcOn = false;
	}
	else
	{
		startTime = time(0);
		sensorInterrupt->calcDistance(true);
		calcOn = true;
	}
}

// Set measurement units
void Calculator::setUnits()
{
	if(units == KILOMETERS)
	{
		ledLight->setUnitLightOnOff(true);
		units = MILES;
	}
	else
	{
		ledLight->setUnitLightOnOff(false);
		units = KILOMETERS;
	}
}

// Gets units
int Calculator::getUnits()
{
	return units;
}
